#include <iostream>
#include "JuliaBrain.h"
#include "IniFile.h"

using namespace std;

struct Complex::ComplexValue* Complex::m_AllValue = NULL;
int Complex::m_nRealMethod = 17;
int Complex::m_nImageMethod = 18;

Complex FindGoodDefault(int good_runid, float &max_rate) {
	float rate;
	int max_i = 0;
	Complex def = Complex( M_E * cos(65.0 / 180.0 * M_PI),
	M_E * sin(65.0 / 180.0 * M_PI));

	JuliaBrain *jb = new JuliaBrain();
	if (good_runid != 0)
		jb->LoadChainDefFromFile(good_runid);

	max_rate = -1;
	for (int i = 1; i < 180; i++) {
		def = Complex( M_E * cos(i / 180.0 * M_PI),
		M_E * sin(i / 180.0 * M_PI));
		rate = jb->RunAllTrainingCase(def);
		if (rate > max_rate) {
			//printf("rt %f at ang:%d\n",rate, i);
			max_rate = rate;
			max_i = i;
		}
	}

	delete jb;
	if (max_i >= 0) {
		def = Complex( M_E * cos(max_i / 180.0 * M_PI),
		M_E * sin(max_i / 180.0 * M_PI));
		CIniFile ini;
		ini.IniInit();
		if (0 == ini.IniOpenFile("Method.ini")) {
			ini.IniSetInteger("Default", "Angle", max_i);

			ini.IniCloseFile();
		}
	}

	printf("good angle %d, rate %f\n", max_i, max_rate);
	return def;
}

int FindGoodChainDefine(Complex def, int runid, int nSigNum, int nNervusNum,
		float &max_rate) {
	float rate;
	int max_i = -1;
	max_rate = -1;

	JuliaBrain *jb = new JuliaBrain();
	if (runid >= 1000 + NERVUS_COUNT * NERVUS_SIGNAL_COUNT) {
		jb->LoadChainDefFromFile(runid);
		runid = 1000;
		jb->SaveChainDefToFile(runid);
	} else if (runid >= 1000) {
		jb->LoadChainDefFromFile(runid);
		max_rate = jb->RunAllTrainingCase(def);
	}

	for (int i = 0; i < NERVUS_COUNT; i++) {
		//printf("Try %d,%d :%d\n",nSigNum ,nNervusNum, i );
		jb->SetChainDef(nSigNum, nNervusNum, i);
		rate = jb->RunAllTrainingCase(def);
		if (rate >= max_rate) {
			max_rate = rate;
			max_i = i;
			if (runid + i >= 1000)
				jb->SaveChainDefToFile(runid + i);
		}
	}

	delete jb;

	printf("## id %d, rate %f at[%d,%d]\n", runid + max_i, max_rate, nSigNum,
			nNervusNum);
	if (max_i >= 0)
		return max_i + runid;
	else
		return runid;
}

class AllNum;
class TryNum {
public:
	TryNum(int size) {
		nSize = size;
		pNums = new int[size];
		for (int j = 0; j < size; j++)
			pNums[j] = 0;
	}
	;
	~TryNum() {
		delete pNums;
	}
	;

	void TravelAll(AllNum* pAllNum, int size);
private:
	int *pNums;
	int nSize;
};

struct NumList {
	int *pNums;
	struct NumList *next;
};
class AllNum {
public:
	AllNum(int size) {
		nSize = size;
		m_pCurrentNum = new struct NumList();
		m_pNumList = m_pCurrentNum;
	}
	;
	~AllNum() {
		struct NumList* pNext;
		m_pCurrentNum = m_pNumList;
		do {
			pNext = m_pCurrentNum->next;
			delete m_pCurrentNum->pNums;
			delete m_pCurrentNum;
			m_pCurrentNum = pNext;
		} while (m_pCurrentNum);
	}
	;

	void BuildList(void) {
		TryNum tn(nSize);
		tn.TravelAll(this, nSize);
		m_pCurrentNum = m_pNumList;
	}
	;

	void SaveNum(int* pNums) {
		m_pCurrentNum->pNums = new int[nSize];
		memcpy(m_pCurrentNum->pNums, pNums, sizeof(int) * nSize);
		m_pCurrentNum->next = new struct NumList();
		m_pCurrentNum = m_pCurrentNum->next;
		m_pCurrentNum->pNums = NULL;
	}
	;

	int* GetNextNum(void) {
		if (m_pCurrentNum == NULL)
			return NULL;
		int* pNums = m_pCurrentNum->pNums;
		m_pCurrentNum = m_pCurrentNum->next;
		return pNums;
	}
	;
private:
	struct NumList* m_pNumList;
	struct NumList* m_pCurrentNum;
	int nSize;
};

void TryNum::TravelAll(AllNum* pAllNum, int size) {
	if (size == 1) {
		for (int i = 0; i < NERVUS_COUNT; i++) {
			pNums[0] = i;
			pAllNum->SaveNum(pNums);
		}
	} else {
		for (int i = 0; i < NERVUS_COUNT; i++) {
			pNums[size - 1] = i;
			TravelAll(pAllNum, size - 1);
		}
	}
}

int FindGoodChainDefine2(Complex def, int runid, int nNervusNum,
		float &max_rate) {
	float rate;
	int max_i = -1;
	int *pToNervusNums[NERVUS_CHAIN_COUNT];
	max_rate = -1;

	JuliaBrain *jb = new JuliaBrain();
	if (runid >= 1000 + NERVUS_COUNT * NERVUS_SIGNAL_COUNT) {
		jb->LoadChainDefFromFile(runid);
		runid = 1000;
		jb->SaveChainDefToFile(runid);
	} else if (runid >= 1000) {
		jb->LoadChainDefFromFile(runid);
		max_rate = jb->RunAllTrainingCase(def);
	}

	jb->SaveChainDefToFile(1);
	AllNum *tn[NERVUS_CHAIN_COUNT];

	for (int j = 0; j < NERVUS_CHAIN_COUNT; j++) {
		tn[j] = new AllNum(NERVUS_SIGNAL_COUNT - 1);
		tn[j]->BuildList();
	}

	while ((pToNervusNums[0] = tn[0]->GetNextNum()) != NULL) {
		jb->LoadChainDefFromFile(1);

		jb->SetOneChainDef(0, nNervusNum, pToNervusNums[0]);
		//printf("%d to %d-%d-%d\n",
		//		nNervusNum, *(pToNervusNums[0]), *(pToNervusNums[0] + 1),
		//		*(pToNervusNums[0] + 2));
		for (int j = 1; j < NERVUS_CHAIN_COUNT; j++) {
			pToNervusNums[j] = tn[j]->GetNextNum();
			jb->SetOneChainDef(j, nNervusNum, pToNervusNums[j]);
			//printf("%d to %d-%d-%d;",
			//		nNervusNum, *(pToNervusNums[j]), *(pToNervusNums[j] + 1),
			//		*(pToNervusNums[j] + 2));
		}
		rate = jb->RunAllTrainingCase(def);

		if (rate > max_rate) {
			max_i++;
			printf("%d to %d-%d-%d;",
					nNervusNum, *(pToNervusNums[0]), *(pToNervusNums[0] + 1),
					*(pToNervusNums[0] + 2));
			printf("rate %f, id: %d, angle[%f]\n", rate,
					runid + max_i, def.Angle());
			max_rate = rate;
			if (runid + max_i >= 1000) {
				jb->SaveChainDefToFile(runid + max_i);
				jb->PrintChainDef();
				jb->SaveChainDefToFile(10000);
			}
			jb->SaveChainDefToFile(1);
		}
	}

	for (int j = 0; j < NERVUS_CHAIN_COUNT; j++)
		delete tn[j];
	delete jb;

	printf("#-# id %d, rate %f at[%d]\n", runid + max_i, max_rate, nNervusNum);
	fflush(stdout);
	if (max_i >= 0)
		return max_i + runid;
	else
		return runid;
}

void do_it() {
	int angle;
	Complex def = Complex( M_E * cos(65.0 / 180.0 * M_PI),
	M_E * sin(65.0 / 180.0 * M_PI));
	float max_rate = 0.0;

	CIniFile ini;
	ini.IniInit();
	if (0 == ini.IniOpenFile("Method.ini")) {
		Complex::m_nRealMethod = ini.IniGetInteger("Method", "real", 0);
		Complex::m_nImageMethod = ini.IniGetInteger("Method", "image", 0);
		angle = ini.IniGetInteger("Default", "Angle", 65);
		def = Complex( M_E * cos(angle / 180.0 * M_PI),
		M_E * sin(angle / 180.0 * M_PI));
		ini.IniCloseFile();
		printf("Got method %d,%d,[%d]\n", Complex::m_nRealMethod,
				Complex::m_nImageMethod, angle);
	}

	JuliaBrain *jb = new JuliaBrain();
	if (jb->LoadChainDefFromFile(10000)) {
		max_rate = jb->RunAllTrainingCase(def);
		jb->PrintChainDef();
	}
	delete jb;

	printf("rate is %f\n", max_rate);

}

void try_it(int tm) {
	int runid = 999;
	Complex def = Complex( M_E * cos(65.0 / 180.0 * M_PI),
	M_E * sin(65.0 / 180.0 * M_PI));
	float max_rate = 0.0;

	CIniFile ini;
	ini.IniInit();
	if (0 == ini.IniOpenFile("Method.ini")) {
		Complex::m_nRealMethod = ini.IniGetInteger("Method", "real", 0);
		Complex::m_nImageMethod = ini.IniGetInteger("Method", "image", 0);
		ini.IniCloseFile();
		printf("Got method %d,%d\n", Complex::m_nRealMethod,
				Complex::m_nImageMethod);
	}

	for (int j = 0; j < tm; j++) {
		printf("!!!!!!!!!!round %d\n", j);
		for (int i = 0; i < NERVUS_COUNT; i++) {
			runid = FindGoodChainDefine2(def, runid, i, max_rate);
		}

		def = FindGoodDefault(runid, max_rate);
	}

}

float TryMethod(void) {
	int runid = 999;
	float max_rate = 0.0;
	Complex def = Complex( M_E * cos(65.0 / 180.0 * M_PI),
	M_E * sin(65.0 / 180.0 * M_PI));

	for (int j = 0; j < 4; j++) {
		printf("!!!!!!!!!!round %d\n", j);
		for (int i = 0; i < NERVUS_COUNT; i++) {
			runid = FindGoodChainDefine2(def, runid, i, max_rate);
		}

		def = FindGoodDefault(runid, max_rate);
	}
	printf("========[Method %d,%d] rate: %f========\n", Complex::m_nRealMethod,
			Complex::m_nImageMethod, max_rate);
	return max_rate;
}

void prepare_run_dir(void);

int main() {
	Complex::InitAllValue();

	//prepare_run_dir();
	//do_it();
	//try_it(4);

#if 1
	Complex::m_nRealMethod =8;
	Complex::m_nImageMethod= 41;
	 //for( Complex::m_nRealMethod=0; Complex::m_nRealMethod<=95; Complex::m_nRealMethod++ )
	 //for( Complex::m_nImageMethod=0; Complex::m_nImageMethod<=95; Complex::m_nImageMethod++ )
	 {
		TryMethod( );
	 }
#endif

	Complex::ReleaseAllValue();
	return 0;
}

struct _good_method {
	int r;
	int i;
} gm[] = { { 4, 34 }, { 21, 18 }, { 26, 34 }, { 26, 77 }, { 26, 83 }, { 27, 5 },
		{ 35, 65 }, { 36, 64 }, { 42, 50 }, { 43, 30 }, { 43, 56 }, { 48, 20 },
		{ 48, 26 }, { 48, 27 }, { 48, 34 }, { 48, 56 }, { 48, 58 }, { 48, 59 },
		{ 48, 78 }, { 48, 94 }, { 51, 83 }, { 57, 27 }, { 57, 43 }, { 57, 49 },
		{ 57, 83 }, { 59, 43 }, { 59, 83 }, { 65, 35 }, { 82, 58 }, { 91, 49 } };

void prepare_run_dir(void) {
	system("mkdir run");
	for (int i = 0; i < sizeof(gm) / sizeof(struct _good_method); i++) {
		char cmd[100];
		sprintf(cmd, "mkdir run/dir_%d", i);
		system(cmd);
		sprintf(cmd, "touch run/dir_%d/Method.ini", i);
		system(cmd);
		sprintf(cmd,
				"ln -s ../../Release/ResultAnalyzer run/dir_%d/ResultAnalyzer ",
				i);
		system(cmd);

		sprintf(cmd, "run/dir_%d/Method.ini", i);
		CIniFile ini;
		ini.IniInit();
		if (0 == ini.IniOpenFile(cmd)) {
			ini.IniSetInteger("Method", "real", gm[i].r);
			ini.IniSetInteger("Method", "image", gm[i].i);
			ini.IniSetInteger("Default", "Angle", 65);

			ini.IniCloseFile();
		}
	}

}
